﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using Flame.Reflection;

namespace Flame.Dlr
{
    public class Manager : IExecEngines
    {
        Dictionary<Type, IExecutable> _languagesMap;

        public ScopeParameters ScopeParameters { get; private set; }
        public event EventHandler<InputEventArg> OnInput;
        public event EventHandler<StreamOutput.StreamOutputEventArg> OnOutput;
        public event EventHandler<ResultEventArg> OnResult;
        public event EventHandler<ErrorEventArg> OnError;
        public event EventHandler<ClearedScopeEventArgs> OnClearedScope;

        ManagerSettings _settings = null;

        static Manager()
        {
            Flame.Base.DlrPluginLoader.LoadAll(PathsHelper.GetMainPath("DLR"));
        }

#if NET35
#else
        public dynamic DShare { get; private set; }
#endif
        public Share Share { get; private set; }

       /* public Manager(ScopeParameters scopeParameter)
        {
            ScopeParameters = scopeParameter;
            Share = new Share();
#if NET35
#else
            DShare = new System.Dynamic.ExpandoObject();
#endif

            _settings = new ManagerSettings();
            GetAllExecutable(_settings);
            buildAll();
        }*/


        public Manager(ScopeParameters scopeParameter, ManagerSettings settings)
        {
            ScopeParameters = scopeParameter;
            _settings = settings;


            Share = new Share();
#if NET35
#else
            DShare = new System.Dynamic.ExpandoObject();
#endif


            buildAll();
        }

        public void AddVariable(Variable variable)
        {
            foreach (IExecutable exec in _languagesMap.Values)
            {
                if (exec != null)
                    exec.AddVariable(variable);
            }
        }

        public void Clear()
        {
            foreach (IExecutable exec in _languagesMap.Values)
                exec.ClearScope(ScopeParameters);
        }

        public void Rebuild()
        {
            buildAll();
        }

        public static IExecutable GetNewEngine(string language)
        {
            Type t = Languages.Get(language);
            return (IExecutable)Activator.CreateInstance(t);
        }

        IExecutable tryFindEngine(Type type)
        {

            IExecutable exec = null;
            try
            {
                exec = (IExecutable)Activator.CreateInstance(type);// Activator.CreateInstance(); 
            }
            catch(Exception e)
            {
                return new ExceptionExec(type.Name, e);
            }

            //if (!(exec is IExecutable)) return new ExceptionExec(name);

            if(exec is ICompilable)
            {
                foreach (var y in _languagesMap.Values)
                {
                    (exec as ICompilable).AddAssemblyDestination(y);
                }    
            }

            foreach (var x in _languagesMap.Values)
            {
                ICompilable cx = x as ICompilable;

                if (cx != null)
                {
                    cx.AddAssemblyDestination(exec);
                }
            }

            _languagesMap.Add(type, exec);
            exec.ClearScope(ScopeParameters);
            return exec;
        }

        public override IExecutable GetEngine(Type type)
        {
            if (!_languagesMap.ContainsKey(type)) return tryFindEngine(type);
            return _languagesMap[type];
        }
        /*public IExecutable GetEngine(string nametype)
        {
            GetEngine(Languages.Get(nametype));
        }*/

        public Result Execute(Script script)
        {
            return GetEngine(script.Language).Execute(script.Code);
        }

        

        void addEvents(ExecutableWithEvent exec)
        {
            exec.OnOutput += (s, e) => { if (OnOutput != null) OnOutput(s, e); };
            exec.OnError += (s, e) => { if (OnError != null) OnError(s, e); };
            exec.OnInput += (s, e) => { if (OnInput != null) OnInput(s, e); };
            exec.OnResult += (s, e) => { if (OnResult != null) OnResult(s, e); };
            exec.OnClearedScope += (s, e) =>  { if (OnClearedScope != null)  OnClearedScope(s, e); };
        }

        IExecutable addScriptingLanguage_WithEvent(IExecutable exec)
        {
           // if (Languages.Languages_All.Contains(language))
            {

                IExecutable c;

                try
                {
                    c = new ExecutableWithEvent(exec);
                    addEvents(c as ExecutableWithEvent);
                }
#if DEBUG
                catch (System.Exception e)
                {
                    System.Diagnostics.Debug.WriteLine(e.Message);
                    c = null;
                }
#else
                catch 
                {
                    c = null;
                }

#endif
                return c;
            }
        }

        void add(IExecutable exec)
        {
            _languagesMap.Add(exec.GetType(), exec);
        }

        

        void buildAll()
        {
            _languagesMap = new Dictionary<Type, IExecutable>();


            foreach (var exec in _settings.CompilersType)
            {
                ICompilable c = (ICompilable)Activator.CreateInstance(exec);
                _languagesMap.Add(c.GetType(), c);
            }

            foreach (var exec in _settings.ExecsType)
            {
                IExecutable c = (IExecutable)Activator.CreateInstance(exec);
                //if (! (exec is ICompilable))
                {
                    IExecutable newexec = addScriptingLanguage_WithEvent(c);
                    if (newexec != null)
                        _languagesMap.Add(c.GetType(), newexec);
                }
            }

            foreach (var x in _languagesMap.Values)
            {
                ICompilable cx = x as ICompilable;

                if (cx != null)
                {
                    foreach (var y in _languagesMap.Values)
                    {
                        if (x != y)
                        {
                            cx.AddAssemblyDestination(y);
                        }
                    }
                }
            }

           /* foreach (var cx in compilers)
            {
                foreach (var e in execsType)
                {
                    cx.AddAssemblyDestination(e);
                }
            }*/

        }


        /*Dictionary<Type, IIntellisense> _intellisenseMap = null;
        public Dictionary<Type, IIntellisense> Intellisense
        {
            get 
            {
                if (_intellisenseMap == null)
                {
                    _intellisenseMap = new Dictionary<Type, IIntellisense>();
                    Type t = Languages.Get("IronRubyExec");
                    var exec = compilerManager.GetEngine(t);
                    _intellisenseMap.Add(t, new RubyIntellisense(exec));
                    t = Languages.Get("IronPythonExec");
                    exec = compilerManager.GetEngine(t);
                    _intellisenseMap.Add(t, new PythonIntellisense(exec));
                }

                return _intellisenseMap;
            }
        }*/

        //Dictionary<Type, IIntellisense> _intellisenseMap = new Dictionary<Type, IIntellisense>();
        


    }
}
